1 /**
2  * This module is a submodule of devisualization.image
3  *
4  * Provides optional interfaces which declares an class based image as well as reusable concepts such as a swappable image implementation dependent upon a provided image implementation.
5  * 
6  * License:
7  *              Copyright Devisualization (Richard Andrew Cattermole) 2014 - 2017.
8  *     Distributed under the Boost Software License, Version 1.0.
9  *        (See accompanying file LICENSE_1_0.txt or copy at
10  *              http://www.boost.org/LICENSE_1_0.txt)
11  */
12 module devisualization.image.interfaces;
13 import devisualization.image.primitives;
14 import std.experimental.color;
15 import stdx.allocator : IAllocator, ISharedAllocator, theAllocator, processAllocator;
16 import std.traits : isPointer, PointerTarget, isUnsigned;
17 
18 /**
19  * Interface that defines the root methods required for a class/struct to be an image storage type.$(BR)
20  * Similar in purpose as InputRange is to ranges.
21  * 
22  * Must have a constructor that takes in the width and height as arguments.
23  * ------------
24  *  this(size_t x, size_t y, IAllocator allocator = theAllocator())
25  * ------------
26  * Optionally may receive an allocator for allocating the pixel data from. The default is via the garbage collector.
27  *
28  * The width and height should not be 0. Instead for dummy test code use 1.$(BR)
29  * Fields cannot be used in place of methods.
30  */
31 interface ImageStorage(Color) if (isColor!Color) {
32     @property {
33         /// Gets the width of the image
34 		size_t width() @nogc nothrow @safe;
35 		/// Ditto
36 		size_t width() @nogc nothrow @safe shared;
37         
38         /// Gets the height of the image
39 		size_t height() @nogc nothrow @safe;
40 		/// Ditto
41 		size_t height() @nogc nothrow @safe shared;
42     }    
43 
44     /**
45      * Get a pixel given the position.
46      *
47      * Params:
48      *      x   =   X position in image
49      *      y   =   Y position in image
50      *
51      * Throws:
52      *      If $(D x) or $(D y) coordinate is outside of the image boundries.
53      *
54      * Returns:
55      *      The pixel color at point.
56      */
57 	Color getPixel(size_t x, size_t y) @nogc @safe;
58 	/// Ditto
59 	Color getPixel(size_t x, size_t y) @nogc @safe shared;
60 
61     /**
62      * Sets a pixel given the position.
63      *
64      * Params:
65      *      x       =   X position in image
66      *      y       =   Y position in image
67      *      value   =   The color to assign to the pixel
68      *
69      * Throws:
70      *      If $(D x) or $(D y) coordinate is outside of the image boundries.
71      */
72 	void setPixel(size_t x, size_t y, Color value) @nogc @safe;
73 	/// Ditto
74 	void setPixel(size_t x, size_t y, Color value) @nogc @safe shared;
75 
76     /**
77      * Get a pixel given the position.
78      *
79      * Params:
80      *      x   =   X position in image
81      *      y   =   Y position in image
82      *
83      * Throws:
84      *      If $(D x) or $(D y) coordinate is outside of the image boundries.
85      *
86      * Returns:
87      *      The pixel color at point.
88      *
89      * See_Also:
90      *      getPixel
91      */
92 	Color opIndex(size_t x, size_t y) @nogc @safe;
93 	/// Ditto
94 	Color opIndex(size_t x, size_t y) @nogc @safe shared;
95 
96     /**
97      * Sets a pixel given the position.
98      *
99      * Params:
100      *      x       =   X position in image
101      *      y       =   Y position in image
102      *      value   =   The color to assign to the pixel
103      *
104      * Throws:
105      *      If $(D x) or $(D y) coordinate is outside of the image boundries.
106      *
107      * See_Also:
108      *      setPixel
109      */
110 	void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe;
111 	/// Ditto
112 	void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe shared;
113 
114     /**
115      * Resizes the data store.
116      *
117      * Will not scale and $(U may) lose data in the process.
118      *
119      * Params:
120      *      newWidth    =   The width the data store will become
121      *      newHeight   =   The height the data store will become
122      *
123      * Returns:
124      *      If the data store was successfully resized
125      */
126 	bool resize(size_t newWidth, size_t newHeight) @safe;
127 	/// Ditto
128 	bool resize(size_t newWidth, size_t newHeight) @safe shared;
129 }
130 
131 /**
132  * Adds the ability to get a pixel based upon its offset.
133  *
134  * X and Y coordinate can be calculate using:
135  * -----------------
136  * ImageStorage!Color image = ...;
137  * size_t offset = ...;
138  * size_t x = offset % image.height;
139  * size_t y = offset / image.height;
140  * -----------------
141  * 
142  * Offset from X and Y coordinate can be calculated as:
143  * -----------------
144  * ImageStorage!Color image = ...;
145  * size_t x, y = ...;
146  * size_t offset = x + (y * image.width);
147  * -----------------
148  *
149  * See_Also:
150  *      ImageStorage
151  */
152 interface ImageStorageOffset(Color) {
153     @property {
154         /// The number of pixels in total
155 		size_t count() @nogc nothrow @safe;
156 		/// Ditto
157 		size_t count() @nogc nothrow @safe shared;
158     }
159 
160     /**
161      * Get a pixel given the position.
162      *
163      * Params:
164      *      offset  =   The offset of the pixel
165      *
166      * Throws:
167      *      If $(D offset) coordinate is outside of the image boundary.
168      *
169      * Returns:
170      *      The pixel color at point.
171      */
172 	Color getPixelAtOffset(size_t offset) @nogc @safe;
173 	/// Ditto
174 	Color getPixelAtOffset(size_t offset) @nogc @safe shared;
175 
176     /**
177      * Set a pixel given the position.
178      *
179      * Params:
180      *      offset  =   The offset of the pixel
181      *      value   =   The color to assign to the pixel
182      *
183      * Throws:
184      *      If $(D offset) coordinate is outside of the image boundary.
185      */
186 	void setPixelAtOffset(size_t offset, Color value) @nogc @safe;
187 	/// Ditto
188 	void setPixelAtOffset(size_t offset, Color value) @nogc @safe shared;
189 
190     /**
191      * Get a pixel given the position.
192      *
193      * Params:
194      *      offset  =   The offset of the pixel
195      *
196      * Throws:
197      *      If $(D offset) coordinate is outside of the image boundary.
198      *
199      * Returns:
200      *      The pixel color at point.
201      *
202      * See_Also:
203      *      getPixelAtOffset
204      */
205 	Color opIndex(size_t offset) @nogc @safe;
206 	/// Ditto
207 	Color opIndex(size_t offset) @nogc @safe shared;
208 
209     /**
210      * Set a pixel given the position.
211      *
212      * Params:
213      *      offset  =   The offset of the pixel
214      *      value   =   The color to assign to the pixel
215      *
216      * Throws:
217      *      If $(D offset) coordinate is outside of the image boundary.
218      *
219      * See_Also:
220      *      setPixelAtOffset
221      */
222 	void opIndexAssign(Color value, size_t offset) @nogc @safe;
223 	/// Ditto
224 	void opIndexAssign(Color value, size_t offset) @nogc @safe shared;
225 }
226 
227 /**
228  * Wraps an image implementation up so that the exact implementation doesn't matter.
229  * As long as the color type is known it can be passed around freely.
230  * It does $(I not) allocate to perform its functions and can be safely used on the stack.
231  */
232 struct SwappableImage(Color) if (isColor!Color) {
233     this() @disable;
234     this(size_t width, size_t height, IAllocator alloc=theAllocator()) @disable;
235 
236     ~this() @safe {
237         if (destroyerDel !is null) {
238             destroyerDel();
239         }
240     }
241 
242     /**
243      * Constructs a swappable image using a specific image storage type as its value.
244      *
245      * If the image storage type does not support ImageStorageOffset it will add support.$(BR)
246      * If the color type specified is not the same type for the image storage instance then it'll auto convert. It is the responsiblity of the user to check for precision gain/lost.$(BR)
247      * Supports deallocating of the image provided, using the allocator, when the destructor is called. If the allocator is not provided it will not deallocate it.
248      *
249      * Params:
250      *      fromImage   =   The instance to take delegates from
251      *      allocator   =   Allocator to deallocate the image if provided when destructor is called
252      * 
253      * See_Also:
254      *      ImageStorage, ImageStorageOffset
255      */
256 	this(T)(T from, IAllocator allocator = null) @nogc @trusted if (((!isPointer!T && isImage!T) || (isPointer!T && isImage!(PointerTarget!T))) &&
257 		isUnsigned!(ImageIndexType!T) && (ImageIndexType!T).sizeof <= (void*).sizeof)
258     in {
259         static if (is(T == class))
260             assert(from !is null);
261     } body {
262         static if (isPointer!T && isImage!(PointerTarget!T))
263             alias ImageRealType = PointerTarget!T;
264         else
265             alias ImageRealType = T;
266 
267         static if (!isPointer!T && is(ImageRealType == struct))
268             origin_ = cast(void*)&from;
269         else
270             origin_ = cast(void*)from;
271 
272         this.allocator = allocator;
273         if (allocator !is null) {
274             static if (!isPointer!T && is(T == struct))
275                 destroyerDel = &destroyerHandler!(ImageRealType*);
276             else
277                 destroyerDel = &destroyerHandler!T;
278         }
279 
280         widthDel = &from.width;
281         heightDel = &from.height;
282         resizeDel = cast(bool delegate(size_t, size_t) @safe)&from.resize;
283 
284         pixelAt_ = cast(void delegate())&from.getPixel;
285         pixelAtDel = &pixelAtCompatFunc!(ImageColor!ImageRealType);
286 
287         pixelStoreAt_ = cast(void delegate())&from.setPixel;
288         pixelStoreAtDel = &pixelStoreAtCompatFunc!(ImageColor!ImageRealType);
289 
290         static if (supportsImageOffset!ImageRealType) {
291             countDel = &from.count;
292 
293             pixelAtOffset_ = cast(void delegate())&from.getPixelAtOffset;
294             pixelAtOffsetDel = &pixelAtOffsetCompatFunc!(ImageColor!ImageRealType);
295             
296             pixelStoreAtOffset_ = cast(void delegate())&from.setPixelAtOffset;
297             pixelStoreAtOffsetDel = &pixelStoreAtOffsetCompatFunc!(ImageColor!ImageRealType);
298         } else {
299             countDel = &countNotSupported;
300             pixelAtOffsetDel = &pixelAtOffsetNotSupported;
301             pixelStoreAtOffsetDel = &pixelStoreAtOffsetNotSupported;
302         }
303     }
304 
305     @property {
306         // Gets the width of the image
307         size_t width() @nogc nothrow @safe { return widthDel(); }
308 
309         /// Gets the height of the image
310         size_t height() @nogc nothrow @safe { return heightDel(); }
311 
312         /// Gets the number of pixels the image contains
313         size_t count() @nogc nothrow @safe { return countDel(); }
314 
315         /**
316          * A pointer to the original image storage type.
317          * Use at your own risk.
318          */
319         void* original() @nogc nothrow @safe { return origin_; }
320     }
321 
322     /**
323      * Get a pixel given the position.
324      *
325      * Params:
326      *      x   =   X position in image
327      *      y   =   Y position in image
328      *
329      * Throws:
330      *      If $(D x) or $(D y) coordinate is outside of the image boundries.
331      *
332      * Returns:
333      *      The pixel color at point.
334      */
335     Color getPixel(size_t x, size_t y) @nogc @safe { return pixelAtDel(x, y); }
336 
337     /**
338      * Sets a pixel given the position.
339      *
340      * Params:
341      *      x       =   X position in image
342      *      y       =   Y position in image
343      *      value   =   The color to assign to the pixel
344      *
345      * Throws:
346      *      If $(D x) or $(D y) coordinate is outside of the image boundries.
347      */
348     void setPixel(size_t x, size_t y, Color value) @nogc @safe { pixelStoreAtDel(x, y, value); }
349 
350     /**
351      * Get a pixel given the position.
352      *
353      * Params:
354      *      offset  =   The offset of the pixel
355      *
356      * Throws:
357      *      If $(D offset) coordinate is outside of the image boundary.
358      *
359      * Returns:
360      *      The pixel color at point.
361      */
362     Color getPixelAtOffset(size_t offset) @nogc @safe { return pixelAtOffsetDel(offset); };
363 
364     /**
365      * Set a pixel given the position.
366      *
367      * Params:
368      *      offset  =   The offset of the pixel
369      *      value   =   The color to assign to the pixel
370      *
371      * Throws:
372      *      If $(D offset) coordinate is outside of the image boundary.
373      */
374     void setPixelAtOffset(size_t offset, Color value) @nogc @safe { pixelStoreAtOffsetDel(offset, value); }
375 
376     /**
377      * Get a pixel given the position.
378      *
379      * Params:
380      *      x   =   X position in image
381      *      y   =   Y position in image
382      *
383      * Throws:
384      *      If $(D x) or $(D y) coordinate is outside of the image boundries.
385      *
386      * Returns:
387      *      The pixel color at point.
388      *
389      * See_Also:
390      *      getPixel
391      */
392     Color opIndex(size_t x, size_t y) @nogc @safe { return getPixel(x, y); }
393 
394     /**
395      * Sets a pixel given the position.
396      *
397      * Params:
398      *      x       =   X position in image
399      *      y       =   Y position in image
400      *      value   =   The color to assign to the pixel
401      *
402      * Throws:
403      *      If $(D x) or $(D y) coordinate is outside of the image boundries.
404      *
405      * See_Also:
406      *      setPixel
407      */
408     void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe { setPixel(x, y, value); }
409 
410     /**
411      * Get a pixel given the position.
412      *
413      * Params:
414      *      offset  =   The offset of the pixel
415      *
416      * Throws:
417      *      If $(D offset) coordinate is outside of the image boundary.
418      *
419      * Returns:
420      *      The pixel color at point.
421      *
422      * See_Also:
423      *      getPixelAtOffset
424      */
425     Color opIndex(size_t offset) @nogc @safe { return getPixelAtOffset(offset); }
426 
427     /**
428      * Set a pixel given the position.
429      *
430      * Params:
431      *      offset  =   The offset of the pixel
432      *      value   =   The color to assign to the pixel
433      *
434      * Throws:
435      *      If $(D offset) coordinate is outside of the image boundary.
436      *
437      * See_Also:
438      *      setPixelAtOffset
439      */
440     void opIndexAssign(Color value, size_t offset) @nogc @safe { setPixelAtOffset(offset, value); }
441 
442     /**
443      * Resizes the data store.
444      *
445      * Will not scale and $(U may) lose data in the process.$(BR)
446      * If the resize operation puts the image into a potentially errornous state, it should throw an exception.
447      *
448      * Params:
449      *      newWidth    =   The width the data store will become
450      *      newHeight   =   The height the data store will become
451      *
452      * Returns:
453      *      If the data store was successfully resized
454      */
455     bool resize(size_t newWidth, size_t newHeight) @safe {
456         return resizeDel(newWidth, newHeight);
457     }
458 
459     private {
460         size_t delegate() @nogc @safe nothrow widthDel;
461         size_t delegate() @nogc @safe nothrow heightDel;
462         size_t delegate() @nogc @safe nothrow countDel;
463 
464         Color delegate(size_t x, size_t y) @nogc @safe pixelAtDel;
465         void delegate(size_t x, size_t y, Color value) @nogc @safe pixelStoreAtDel;
466         Color delegate(size_t offset) @nogc @safe pixelAtOffsetDel;
467         void delegate(size_t offset, Color value) @nogc @safe pixelStoreAtOffsetDel;
468 
469         bool delegate(size_t, size_t) @safe resizeDel;
470 
471         void* origin_;
472 
473         IAllocator allocator;
474         void delegate() @trusted destroyerDel;
475 
476         void delegate() pixelAt_;
477         void delegate() pixelStoreAt_;
478         void delegate() pixelAtOffset_;
479         void delegate() pixelStoreAtOffset_;
480 
481         void destroyerHandler(T)() @trusted {
482             import stdx.allocator : dispose;
483             allocator.dispose(cast(T)origin_);
484         }
485 
486         Color pixelAtCompatFunc(FROM)(size_t x, size_t y) @nogc @trusted {
487             auto del = cast(FROM delegate(size_t x, size_t y) @nogc @safe) pixelAt_;
488             auto got = del(x, y);           
489 
490             static if (is(FROM == Color)) {
491                 return got;
492             } else {
493                 return convertColor!Color(got);
494             }
495         }
496 
497         void pixelStoreAtCompatFunc(FROM)(size_t x, size_t y, Color value) @nogc @trusted {
498             auto del = cast(void delegate(size_t x, size_t y, FROM value) @nogc @safe) pixelStoreAt_;
499 
500             static if (is(FROM == Color)) {
501                 del(x, y, value);
502             } else {
503                 del(x, y, convertColor!FROM(value));
504             }
505         }
506 
507         Color pixelAtOffsetCompatFunc(FROM)(size_t offset) @nogc @trusted {
508             auto del = cast(FROM delegate(size_t offset) @nogc @safe) pixelAtOffset_;
509             
510             static if (is(FROM == Color)) {
511                 return del(offset);
512             } else {
513                 return convertColor!Color(del(offset));
514             }
515         }
516 
517         void pixelStoreAtOffsetCompatFunc(FROM)(size_t offset, Color value) @nogc @trusted {
518             auto del = cast(void delegate(size_t offset, FROM value) @nogc @safe) pixelStoreAtOffset_;
519 
520             static if (is(FROM == Color)) {
521                 del(offset, value);
522             } else {
523                 del(offset, convertColor!FROM(value));
524             }
525         }
526 
527         size_t countNotSupported() @nogc nothrow @safe {
528             return width() * height();
529         }
530 
531         Color pixelAtOffsetNotSupported(size_t offset) @nogc @trusted {
532             return getPixel(offset % height(), offset / (height() + 1));
533         }
534 
535         void pixelStoreAtOffsetNotSupported(size_t offset, Color value) @nogc @trusted {
536             setPixel(offset % height(), offset / (height() + 1), value);
537         }
538     }
539 }
540 
541 version(unittest) package {
542     class MyTestImage(Color) : ImageStorage!Color {
543         private {
544             const size_t width_, height_;
545             IAllocator allocator;
546             Color[][] data;
547         }
548 
549         this(size_t width, size_t height, IAllocator allocator = theAllocator()) {
550             import stdx.allocator : makeArray;
551             this.allocator = allocator;
552 
553             width_ = width;
554             height_ = height;
555             
556             data = allocator.makeArray!(Color[])(width);
557             
558             foreach(_; 0 .. width) {
559                 data[_] = allocator.makeArray!Color(height);
560             }
561         }
562 
563         @property {
564 			size_t width() @nogc nothrow @safe{ return width_; }
565 			size_t width() @nogc nothrow @safe shared{ return width_; }
566 			size_t height() @nogc nothrow @safe { return height_; }
567 			size_t height() @nogc nothrow @safe shared { return height_; }
568         }
569 
570 		Color getPixel(size_t x, size_t y) @nogc @safe { return data[x][y]; }
571 		Color getPixel(size_t x, size_t y) @nogc @safe shared { return data[x][y]; }
572 		void setPixel(size_t x, size_t y, Color value) @nogc @safe { data[x][y] = value; }
573 		void setPixel(size_t x, size_t y, Color value) @nogc shared @safe { data[x][y] = value; }
574 		Color opIndex(size_t x, size_t y) @nogc @safe { return getPixel(x, y); }
575 		Color opIndex(size_t x, size_t y) @nogc @safe shared { return getPixel(x, y); }
576 		void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe { setPixel(x, y, value); }
577 		void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe shared { setPixel(x, y, value); }
578 
579 		bool resize(size_t, size_t) @nogc @safe { return false; }
580 		bool resize(size_t, size_t) @nogc @safe shared { return false; }
581     }
582 }
583 
584 unittest {
585     import stdx.allocator : make, theAllocator;
586 
587     SwappableImage!RGB8 image = SwappableImage!(RGB8)(theAllocator.make!(MyTestImage!RGB8)(8, 3));
588     RGB8 value = image[0, 0];
589 }
590 
591 unittest {
592     import stdx.allocator : make, theAllocator;
593 
594     SwappableImage!RGBA8 image = SwappableImage!(RGBA8)(theAllocator.make!(MyTestImage!RGB8)(8, 3));
595     RGBA8 value = image[0, 0];
596 }
597 
598 unittest {
599     import stdx.allocator : make, theAllocator;
600 
601     SwappableImage!RGB8 image = SwappableImage!(RGB8)(theAllocator.make!(MyTestImage!RGB8)(8, 3));
602     size_t count = image.count();
603     assert(count == 24);
604 }
605 
606 /**
607  * Constructs an input range over an image
608  * For every pixel get x + y and the color value
609  *
610  * TODO: Scan line version of this?
611  *
612  * Params:
613  *      from    =    The image to create a range upon
614  * 
615  * Returns:
616  *      An input range to get every pixel along with its X and Y coordinates.
617  */
618 auto rangeOf(Color)(SwappableImage!Color* from) @nogc nothrow @safe {
619     return RangeOf!Color(from, 0, 0);
620 }
621 
622 /**
623  * Constructs an input range over an image
624  * For every pixel get x + y and the color value
625  *
626  * Will $(B allocate) a new SwappableImage to wrap around the image given on the heap.
627  * The returned input range will auto deallocate the SwappableImage that was allocated when it becomes empty.
628  *
629  * TODO: Scan line version of this?
630  *
631  * Params:
632  *      from        =   The image to create a range upon
633  *      allocator   =   The allocator to allocate the SwappableImage instance on the heap. Will auto free it when destructed.
634  *
635  * Returns:
636  *      An input range to get every pixel along with its X and Y coordinates.
637  */
638 auto rangeOf(Image)(Image from, IAllocator allocator = theAllocator()) @trusted if (isImage!Image) {
639     import stdx.allocator : make;
640     alias Color = ImageColor!Image;
641 
642     SwappableImage!Color* inst = allocator.make!(SwappableImage!Color)(from);
643     return RangeOf!Color(inst, 0, 0, allocator);
644 }
645 
646 private {
647     struct RangeOf(Color) {
648         private {
649             SwappableImage!Color* input;
650             size_t offsetX;
651             size_t offsetY;
652             IAllocator allocator;
653         }
654 
655         private this(SwappableImage!Color* input, size_t offsetX, size_t offsetY, IAllocator allocator = null) @nogc nothrow @trusted {
656             this.input = input;
657             this.offsetX = offsetX;
658             this.offsetY = offsetY;
659             this.allocator = allocator;
660         }
661         
662         @property {
663             auto front() @nogc @safe {
664                 return PixelPoint!Color(input.getPixel(offsetX, offsetY), offsetX, offsetY, input.width, input.height);
665             }
666 
667             bool empty() @trusted {
668                 bool ret = offsetX == 0 && offsetY == input.height();
669                 
670                 if (ret) {
671                     import stdx.allocator : dispose;
672                     // deallocates the input, if the allocator is provided
673 
674                     if (this.allocator !is null && input !is null) {
675                         allocator.dispose(input);
676                         input = null;
677                     }
678                 }
679 
680                 return ret;
681             }
682         }
683 
684         void popFront() @nogc nothrow @safe {
685             if (offsetX == input.width() - 1) {
686                 offsetY++;
687                 offsetX = 0;
688             } else {
689                 offsetX++;
690             }
691         }
692     }
693 }
694 
695 unittest {
696     size_t count;
697 
698     foreach(pixel; new MyTestImage!RGB8(2, 2).rangeOf) {
699         count++;        
700     }
701 
702     assert(count == 4);
703 
704     auto aRange = new MyTestImage!RGB8(2, 2).rangeOf;
705     auto pixel = aRange.front;
706     
707     assert(pixel.x == 0);
708     assert(pixel.y == 0);
709 
710     aRange.popFront;
711 }
712 
713 /**
714  * A single pixel inside an image.
715  * 
716  * To be returned from input ranges.
717  */
718 struct PixelPoint(Color) if (isColor!Color) {
719     ///
720     Color value;
721     alias value this;
722 
723     ///
724     size_t x, y;
725 
726     ///
727     size_t imageWidth, imageHeight;
728 }
729 
730 /// Wraps a struct image storage type into a class
731 final class ImageObject(Impl) : ImageStorage!(ImageColor!Impl) if (is(Impl == struct) && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof) {
732 	import std.traits : Unqual;
733     alias Color = ImageColor!Impl;
734 
735     // If I could make this private, I would...
736     this()(Impl* instance) @trusted {
737         swpInst = instance;
738     }
739 
740 	static if (is(Impl == shared) && __traits(compiles, {new shared Impl(0, 0, cast(shared(ISharedAllocator))null);})) {
741 		// Ditto
742 		this(size_t width, size_t height, shared(ISharedAllocator) allocator = processAllocator()) @trusted shared {
743 			import stdx.allocator : make;
744 			swpInst = cast(shared)allocator.make!(Unqual!Impl)(width, height, allocator);
745 			_salloc = allocator;
746 		}
747 	} else static if (!is(Impl == shared) && __traits(compiles, {new Impl(0, 0, cast(IAllocator)null);})) {
748 		// Ditto
749 		this(size_t width, size_t height, IAllocator allocator = theAllocator()) @trusted {
750 			import stdx.allocator : make;
751 			swpInst = allocator.make!Impl(width, height, allocator);
752 			_alloc = allocator;
753 		}
754 	}
755 
756     @property {
757 		size_t width() @nogc nothrow @safe {return swpInst.width;}
758 		size_t width() @nogc nothrow @trusted shared {return (cast(Unqual!Impl*)swpInst).width;}
759 		size_t height() @nogc nothrow @safe {return swpInst.height;}
760 		size_t height() @nogc nothrow @trusted shared {return (cast(Unqual!Impl*)swpInst).height;}
761     }    
762     
763 	Color getPixel(size_t x, size_t y) @nogc @safe {return swpInst.getPixel(x, y);}
764 	Color getPixel(size_t x, size_t y) @nogc @trusted shared {return (cast(Unqual!Impl*)swpInst).getPixel(x, y);}
765 	void setPixel(size_t x, size_t y, Color value) @nogc @safe {swpInst.setPixel(x, y, value);}
766 	void setPixel(size_t x, size_t y, Color value) @nogc @trusted shared {(cast(Unqual!Impl*)swpInst).setPixel(x, y, value);}
767 	Color opIndex(size_t x, size_t y) @nogc @safe {return swpInst.opIndex(x, y);}
768 	Color opIndex(size_t x, size_t y) @nogc @trusted shared {return (cast(Unqual!Impl*)swpInst).opIndex(x, y);}
769 	void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe {swpInst.opIndexAssign(value, x, y);}
770 	void opIndexAssign(Color value, size_t x, size_t y) @nogc @trusted shared {(cast(Unqual!Impl*)swpInst).opIndexAssign(value, x, y);}
771 	bool resize(size_t newWidth, size_t newHeight) @safe {return swpInst.resize(newWidth, newHeight);}
772 	bool resize(size_t newWidth, size_t newHeight) @trusted shared {return (cast(Unqual!Impl*)swpInst).resize(newWidth, newHeight);}
773 
774     private {
775 		import std.traits : Unqual;
776 
777 		IAllocator _alloc;
778 		shared(ISharedAllocator) _salloc;
779         Impl* swpInst;
780 
781         ~this() {
782             import stdx.allocator : dispose;
783 			static if (is(Impl == shared)) {
784 				if (_salloc !is null)
785 					_salloc.dispose(cast(Unqual!Impl*)swpInst);
786 			} else {
787             	if (_alloc !is null)
788              	   _alloc.dispose(swpInst);
789 			}
790         }
791     }
792 }
793 
794 /**
795  * Constructs an object around an image storage type.
796  *
797  * Params: 
798  *      width       =   The width to assign
799  *      height      =   The height to assign
800  *      allocator   =   Allocator to use
801  *
802  * Returns:
803  *      An ImageObject wrapper around the implementation specified.
804  *
805  * See_Also:
806  *      ImageObject
807  */
808 auto imageObject(Impl)(size_t width, size_t height, IAllocator allocator = theAllocator) @trusted
809 if (is(Impl == struct) && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && !is(Impl==shared)) {
810     import stdx.allocator : make;
811     return allocator.make!(ImageObject!Impl)(width, height, allocator);
812 }
813 
814 /// Ditto
815 auto imageObject(Impl)(size_t width, size_t height, shared(ISharedAllocator) allocator = processAllocator) @trusted
816 if (is(Impl == struct) && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && is(Impl==shared)) {
817 	import stdx.allocator : make;
818 	return allocator.make!(shared(ImageObject!Impl))(width, height, allocator);
819 }
820 
821 /**
822  * Constructs an object around an image storage type.
823  *
824  * Params: 
825  *      instance    =   Instance to wrap
826  *      allocator   =   Allocator to use
827  *
828  * Returns:
829  *      An ImageObject wrapper around the implementation specified.
830  *
831  * See_Also:
832  *      ImageObject
833  */
834 auto imageObject(Impl)(Impl* instance, IAllocator allocator = theAllocator) @trusted
835 if (is(Impl == struct) && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && !is(Impl==shared)) {
836     import stdx.allocator : make;
837     return allocator.make!(ImageObject!Impl)(instance);
838 }
839 
840 /// Ditto
841 auto imageObject(Impl)(Impl* instance, shared(ISharedAllocator) allocator = processAllocator) @trusted
842 if (is(Impl == struct) && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && is(Impl==shared)) {
843 	import stdx.allocator : make;
844 	return allocator.make!(shared(ImageObject!Impl))(instance);
845 }
846 
847 /**
848  * Constructs an object based upon an existing image.
849  *
850  * Params: 
851  *      from        =   Instance to wrap
852  *      allocator   =   Allocator to use
853  *
854  * Returns:
855  *      An ImageObject wrapper around the implementation specified.
856  *
857  * See_Also:
858  *      ImageObject
859  */
860 auto imageObjectFrom(Impl, Image)(Image from, IAllocator allocator = theAllocator) @trusted
861 if (is(Impl == struct) && isImage!Impl && isImage!Image && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && !is(Impl==shared)) {
862     import devisualization.image.primitives : copyTo;
863     import stdx.allocator : make;
864 
865     return from.copyTo(imageObject!Impl(from.width, from.height, allocator));
866 }
867 
868 /// Ditto
869 auto imageObjectFrom(Impl, Image)(Image from, shared(ISharedAllocator) allocator = processAllocator) @trusted
870 if (is(Impl == struct) && isImage!Impl && isImage!Image && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && is(Impl==shared)) {
871 	import devisualization.image.primitives : copyTo;
872 	import stdx.allocator : make;
873 	
874 	return from.copyTo(imageObject!Impl(from.width, from.height, allocator));
875 }